# -*- coding: utf-8 -*-
# @Time    : 2019/5/10 9:40
# @Author  : zhouqiang
# @Site    : 
# @File    : pretty_table.py
# @Software: PyCharm

import os
import csv
import json

import numpy as np
import pandas as pd

import numpy as np
import prettytable
from prettytable import PrettyTable

from quant_researcher.quant.project_tool.celebrity import to_fixed_digit
from quant_researcher.quant.project_tool.localize import BASE_DIR
from quant_researcher.quant.project_tool.logger.my_logger import LOG
from quant_researcher.quant.project_tool.assert_tool import value_in_list, value_is_instance, same_len


def write_str(html_str, t_name, **kwargs):
    # 给每一个单元格都居中显示
    html_str = html_str.replace('<td>', '<td align="center">')

    # 给最前面加上HTML头，指定文件类型，编码格式，要不然可能乱码
    html_title = kwargs.get('html_title', '👓👔👖')
    html_str = '<html lang="en">' \
               '<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">' \
               '<title>%s</title>' \
               '</head>' \
               '<body>' % html_title + html_str
    # 给最前面加上HTML尾
    html_str += '</body>'

    base_dir = kwargs.get('base_dir', BASE_DIR)
    t_name = '%s.html' % t_name
    file_path = os.path.join(base_dir, t_name)
    with open(file_path, 'wb') as f:
        f.write(html_str.encode('utf-8'))
        LOG.info('写入完成：%s' % file_path)


def save(tb, t_name, **kwargs):
    """
    存储一个 prettyTable 到文件

    :param tb: 表
    :param str t_name: 名字
    :param kwargs:
        - titles，str，网页的名字
    :return:
    """
    # {'attributes': {'border': 1, 'align': 'center'}}  'rules': 'all'
    attribute = {'frame': 'hsides', 'align': 'center', 'cellspacing': '6'}
    titles = kwargs.get('titles')

    if isinstance(tb, list):
        len_tb = len(tb)
        LOG.info('需要写入表个数：%s' % len_tb)
        single_or_multiple_html = kwargs.get('single_or_multiple_html', 'single')
        value_in_list(single_or_multiple_html, ['single', 'multiple'])
        if titles:  # 如果指定了存储的表格名，那么个数应该与表的个数一样
            same_len(tb, titles)
        if single_or_multiple_html == 'single':
            value_is_instance(t_name, str)
            # 如果把几个表写到一个html中，需要在每一个表后增加换行符<br>
            html_str = ''
            for idx in range(len_tb):
                a_t = tb[idx]
                table_str = a_t.get_html_string(**{'attributes': attribute})
                if titles:  # 根据idx，把表名加到最后去，不会影响显示效果
                    table_str = table_str.replace('</table>', '<caption>%s</caption></table>' % titles[idx])
                table_str += '<br>' * 2
                html_str += table_str
            write_str(html_str, t_name, **kwargs)
        else:
            value_is_instance(t_name, list)
            same_len(t_name, tb)
            for idx in range(len_tb):
                a_t = tb[idx]
                a_name = t_name[idx]
                html_str = a_t.get_html_string(**{'attributes': attribute})
                if titles:  # 根据idx，把表名加到最后去，不会影响显示效果
                    html_str = html_str.replace('</table>', '<caption>%s</caption></table>' % titles[idx])
                write_str(html_str, a_name, **kwargs)
    else:
        if titles:
            value_is_instance(titles, str)
        html_str = tb.get_html_string(**{'attributes': attribute})
        if titles:  # 根据idx，把表名加到最后去，不会影响显示效果
            html_str = html_str.replace('</table>', '<caption>%s</caption></table>' % titles)
        write_str(html_str, t_name, **kwargs)


def table_to_row(x):
    table_str = x.get_html_string()
    # print('替换前：')
    # print(table_str)
    col_sep = '🔪'
    row_sep = '🚷'
    for to_replace in [
        ' ' * 4,
        ' ' * 8,
        '\n',
        '<table>',
        '</table>',
        '<tr>',
        ('</tr>', row_sep),
        '<td>',
        ('</td>', col_sep),
        '<th>',
        ('</th>', col_sep),
    ]:
        if isinstance(to_replace, str):
            table_str = table_str.replace(to_replace, '')
        else:
            table_str = table_str.replace(to_replace[0], to_replace[1])
    # print('\n替换后：')
    # print(table_str)
    rows = table_str.split(row_sep)
    rows = rows[:-1]
    rows = list(map(lambda x: x.split(col_sep)[:-1], rows))
    # print('总行数：%s' % len(rows))
    # for a_row in rows:
    #     print(a_row)
    return rows


def list_of_dict_to_table(data, cols=None, **kwargs):
    digit = kwargs.get('digit', 5)
    if cols is None:
        cols = data[0].keys()
    x_table = prettytable.PrettyTable(cols)
    for a_dict in data:
        row = [a_dict[a_col] for a_col in cols]
        # 对于float，给个固定位数吧
        row = list(map(lambda x: if_float(x, digit), row))
        x_table.add_row(row)
    return x_table


def ndarray_to_table(npa, obj_name=None):
    """
    把一个矩阵变成 prettyTable

    :param np.ndarray npa: 数据
    :param str obj_name: 名字
    :return: prettyTable
    """
    if not isinstance(npa, np.ndarray):
        if isinstance(npa, list):
            npa = np.array(npa)
        else:
            print('需要一个np.ndarray或者list，但是得到%s' % npa.__class)
    row_count = npa.shape[0]
    col_count = npa.shape[1]
    if obj_name is None:
        msg = '对象维度：%s×%s' % (row_count, col_count)
    else:
        msg = '%s的维度：%s×%s' % (obj_name, row_count, col_count)
    col_limit = 15  # 如果列数太多了，显示出来做什么，根本看不清
    if col_count > col_limit:
        col_count = min(col_count, col_limit)
        msg += '，但是只是显示前%s列' % col_limit
    print(msg)
    x_table = PrettyTable()
    x_table.add_column(fieldname='/', column=[str(x) for x in range(row_count)])
    for a_col in range(col_count):
        x_table.add_column(fieldname=str(a_col), column=list(map(lambda x: if_float(x, digit=4), npa[:, a_col])))
    return x_table


def df_to_table(df, deal_float=False, digit=5):
    """
    把 DF 变成 pretty table，打印观察

    :param df: 数据
    :param int digit: 数据如果保留小数，位数，默认：5
    :param bool deal_float: 是不是要把浮点数处理下
    :return: PrettyTable
    """
    # print(f'是否处理小数：{deal_float}，小数位数指定：{digit}')
    x_table = PrettyTable()
    for ii in df:
        x_table.add_column(
            ii,
            list(map(
                lambda x: if_float(x, digit=digit) if deal_float else x,
                df[ii].values
            ))
        )
    return x_table


def if_float(x, digit):
    return to_fixed_digit(x, digit=digit)

def QA_util_to_json_from_pandas(data):
    """
    explanation:
        将pandas数据转换成json格式

    params:
        * data ->:
            meaning: pandas数据
            type: null
            optional: [null]

    return:
        dict

    demonstrate:
        Not described

    output:
        Not described
    """

    """需要对于datetime 和date 进行转换, 以免直接被变成了时间戳"""
    if 'datetime' in data.columns:
        data.datetime = data.datetime.apply(str)
    if 'date' in data.columns:
        data.date = data.date.apply(str)
    return json.loads(data.to_json(orient='records'))


def QA_util_to_json_from_numpy(data):
    pass


def QA_util_to_json_from_list(data):
    pass


def QA_util_to_list_from_pandas(data):
    """
    explanation:
         将pandas数据转换成列表

    params:
        * data ->:
            meaning: pandas数据
            type: null
            optional: [null]

    return:
        list

    demonstrate:
        Not described

    output:
        Not described
    """

    return np.asarray(data).tolist()


def QA_util_to_list_from_numpy(data):
    """
    explanation:
        将numpy数据转换为列表

    params:
        * data ->:
            meaning: numpy数据
            type: null
            optional: [null]

    return:
        None

    demonstrate:
        Not described

    output:
        Not described
    """

    return data.tolist()


def QA_util_to_pandas_from_json(data):
    """
    explanation:
        将json数据载入为pandas数据

    params:
        * data ->:
            meaning: json数据
            type: null
            optional: [null]

    return:
        DataFrame

    demonstrate:
        Not described

    output:
        Not described
    """
    if isinstance(data, dict):
        return pd.DataFrame(data=[data, ])
    else:
        return pd.DataFrame(data=[{'value': data}])


def QA_util_to_pandas_from_list(data):
    """
    explanation:
        将列表数据转换为pandas

    params:
        * data ->:
            meaning: 列表数据
            type: list
            optional: [null]

    return:
        DataFrame

    demonstrate:
        Not described

    output:
        Not described
    """

    if isinstance(data, list):
        return pd.DataFrame(data=data)